
#include "modules/video_capture/ohos/video_capture_ohos.h"
#include "common_video/ohos/ohos_egl_context_manager.h"
#include "common_video/ohos/ohos_display_manager.h"
#include "rtc_base/logging.h"

#include "third_party/libyuv/include/libyuv/convert.h"

#include <multimedia/image_framework/image_mdk.h>
#include <multimedia/image_framework/image_receiver_mdk.h>
#include <ohcamera/camera_device.h>
#include <ohcamera/video_output.h>
#include <malloc.h>
#include <cmath>

#include <malloc.h>


namespace webrtc {
namespace videocapturemodule {

class ImageReceiverOption {
public:
  ImageReceiverOption(uint32_t width, uint32_t height, uint32_t capacity)
    : width_(width), height_(height), capacity_(capacity) {
    init();
  }
    
  int32_t init() {
    Image_ErrorCode ret = OH_ImageReceiverOptions_Create(&options_);
    if (ret != IMAGE_SUCCESS) {
        RTC_LOG(LS_ERROR) << "OH_ImageReceiverOptions_Create failed with code: " << ret;             
        options_ = nullptr;
        return -1;
    }

    ret = OH_ImageReceiverOptions_SetSize(options_, {width_, height_});
    if (ret != IMAGE_SUCCESS) {
        RTC_LOG(LS_INFO) << "OH_ImageReceiverOptions_SetSize failed with code: " << ret;                
        release();
        return -1;
    }

    ret = OH_ImageReceiverOptions_SetCapacity(options_, capacity_);
    if (ret != IMAGE_SUCCESS) {
        RTC_LOG(LS_ERROR) << "OH_ImageReceiverOptions_SetCapacity failed with code: " << ret;                
        release();
        return -1;
    }
    
    return 0;
  }

  OH_ImageReceiverOptions* GetImageReceiverOptions() {
      return options_;
  }
  
  int32_t release() {
    if (options_) {
        Image_ErrorCode ret = OH_ImageReceiverOptions_Release(options_);
        options_ = nullptr;
        if (ret != IMAGE_SUCCESS) {
          RTC_LOG(LS_ERROR) << "OH_ImageReceiverOptions_Release failed with code: " << ret;                
          return -1;
        }
    }
    
    return 0;
  }

  ~ImageReceiverOption() {
    release();
  }
private:
  uint32_t width_;
  uint32_t height_;
  uint32_t capacity_;
  OH_ImageReceiverOptions *options_ {nullptr};
};  


ImageReceiverOnManager& ImageReceiverOnManager::GetInstance() {
  static ImageReceiverOnManager manager;
  return manager;
}

int32_t ImageReceiverOnManager::AddImageReceiverOn(uint64_t id, OhosImageReceiveOn* imgRecOn) {
  std::unique_lock<std::mutex> lock(map_mutex_);
  receiverOnMap_[id] = imgRecOn;
  return 0;
}

int32_t ImageReceiverOnManager::DelImageReceiverOn(uint64_t id) {
  std::unique_lock<std::mutex> lock(map_mutex_);
  receiverOnMap_.erase(id);
  return 0;
}

OhosImageReceiveOn* ImageReceiverOnManager::GetReceiverOnCb(const uint64_t imageReceiverID) {
  std::map<uint64_t, OhosImageReceiveOn* >::iterator iter;
  std::unique_lock<std::mutex> lock(map_mutex_);
  iter = receiverOnMap_.find(imageReceiverID);
  if (iter == receiverOnMap_.end()) {
      RTC_LOG(LS_ERROR) << "GetReceiverOnCb not find key: " << imageReceiverID;              
      return nullptr;
  }
  auto imgRecOnPtr = iter->second;
  return imgRecOnPtr;
}

void ImageReceiverOnManager::ImageReceiverCallback(OH_ImageReceiverNative* receiver) {
  uint64_t imageReceiverID = OhosImageReceiveOn::GetImageReceiverSurfaceID(receiver);
  if (imageReceiverID == 0) {
      return;
  }
  
  OhosImageReceiveOn* ohosCamera = ImageReceiverOnManager::GetInstance().GetReceiverOnCb(imageReceiverID);
  if (ohosCamera) {
      ohosCamera->ImageReceiverCallback(receiver);
  }
}

uint64_t OhosImageReceiveOn::GetImageReceiverSurfaceID(OH_ImageReceiverNative *receiver) {
  uint64_t id;
  Image_ErrorCode ret = OH_ImageReceiverNative_GetReceivingSurfaceId(receiver, &id);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_GetReceivingSurfaceId failed with code: " << ret;
    return 0;
  }
  return id;
}

uint64_t OhosImageReceiveOn::GetImageReceiverID() {
  return imageReceiverID_;
}

OH_ImageReceiverNative* OhosImageReceiveOn::GetImageReceiver() {
  return imageReceiverNative_;
}

int32_t VideoCaptureOhos::ImageReceiverCallback(OH_ImageReceiverNative* receiver) {
  OH_ImageNative *image;
  Image_ErrorCode ret = OH_ImageReceiverNative_ReadLatestImage(receiver, &image);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_ReadLatestImage failed with code: " << ret;                
    return -1;
  }
  
  Image_Size imageSize;
  ret = OH_ImageNative_GetImageSize(image, &imageSize);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetImageSize failed with code: " << ret;              
    OH_ImageNative_Release(image);
    return -1;
  }

  uint32_t* types;
  size_t typeSize;
  ret = OH_ImageNative_GetComponentTypes(image, nullptr, &typeSize);
  if (ret != IMAGE_SUCCESS || typeSize <= 0) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetComponentTypes typeSize failed with code: " << ret;               
    OH_ImageNative_Release(image);
    return -1;
  }
  
  types = new uint32_t[typeSize];
  ret = OH_ImageNative_GetComponentTypes(image, &types, &typeSize);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetComponentTypes types failed with code: " << ret;               
    OH_ImageNative_Release(image);
    delete [] types;
    return -1;
  }

  int32_t rowStride;
  ret = OH_ImageNative_GetRowStride(image, types[0], &rowStride);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetRowStride failed with code: " << ret;               
    OH_ImageNative_Release(image);
    delete[] types;
    return -1;
  }

  uint8_t *addr = nullptr;
  OH_NativeBuffer *buffer;
  size_t bufferSize;
  ret = OH_ImageNative_GetByteBuffer(image, types[0], &buffer);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetByteBuffer failed with code: " << ret;               
    OH_ImageNative_Release(image);
    delete[] types;
    return -1;
  }
  ret = OH_ImageNative_GetBufferSize(image, types[0], &bufferSize);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_GetBufferSize failed with code: " << ret;                  
    OH_ImageNative_Release(image);
    delete[] types;
    return -1;
  }

  delete[] types;
  int32_t retInt = OH_NativeBuffer_Map(buffer, (void**)&addr);
  if (retInt != 0) {
    RTC_LOG(LS_ERROR) << "OH_NativeBuffer_Map failed with code: " << retInt;                                    
    OH_ImageNative_Release(image);
    return -1;
  }

  ImageReceiverOn(addr, imageSize.width, imageSize.height, rowStride, bufferSize);
  
  ret = OH_ImageNative_Release(image);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageNative_Release failed with code: " << ret;               
    return -1;
  }
  
  return 0;
}

bool VideoCaptureOhos::ImageReceiverOn(uint8_t *buffer, int32_t width, int32_t height, int32_t stride, size_t bufferSize) {
  height = (height > 0) ? height : -height; // abs
  width = stride;
  size_t size = stride * height + stride * height / 2;
  if (bufferSize < size) {
    return false;
  }

  rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer = webrtc::I420Buffer::Create(width, height);
  libyuv::NV21ToI420(buffer, width, buffer + width * height, width, i420_buffer.get()->MutableDataY(),
                     i420_buffer.get()->StrideY(), i420_buffer.get()->MutableDataU(), i420_buffer.get()->StrideU(),
                     i420_buffer.get()->MutableDataV(), i420_buffer.get()->StrideV(), width, height);

  webrtc::VideoFrame video_frame = webrtc::VideoFrame::Builder()
                                     .set_video_frame_buffer(i420_buffer)
                                     .set_timestamp_rtp(0)
                                     .set_timestamp_ms(rtc::TimeMillis())
                                     .set_rotation(webrtc::VideoRotation(GetFrameRotation()))
                                     .build();
  IncomingFrame(video_frame);

  return true;
}

int32_t OhosImageReceiveOn::InitImageReceiver(int width, int height) {
  int32_t returnCode = -1;
  ImageReceiverOption imageReceiverOption(width, height, 8);
  Image_ErrorCode ret = OH_ImageReceiverNative_Create(imageReceiverOption.GetImageReceiverOptions(), &imageReceiverNative_);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_Create failed with code: " << ret;              
    return returnCode;
  }
  
  ret = OH_ImageReceiverNative_On(imageReceiverNative_, ImageReceiverOnManager::ImageReceiverCallback);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_On failed with code: " << ret;     
    return returnCode;
  }
  
  imageReceiverID_ = GetImageReceiverSurfaceID(imageReceiverNative_);
  if (imageReceiverID_ == 0) {
    RTC_LOG(LS_ERROR) << "GetImageReceiverSurfaceID";
    return returnCode;
  }

  ImageReceiverOnManager::GetInstance().AddImageReceiverOn(imageReceiverID_, this);
  returnCode = 0;
  return returnCode;
}

int32_t OhosImageReceiveOn::ImageReceiverRelease() {
  if (imageReceiverNative_ == nullptr) {
      return 0;
  }
  Image_ErrorCode ret = OH_ImageReceiverNative_Off(imageReceiverNative_);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_Off failed with code: " << ret;     
  }
  
  ret = OH_ImageReceiverNative_Release(imageReceiverNative_);
  if (ret != IMAGE_SUCCESS) {
    RTC_LOG(LS_ERROR) << "OH_ImageReceiverNative_Release failed with code: " << ret;                
  }
  ImageReceiverOnManager::GetInstance().DelImageReceiverOn(imageReceiverID_);
  return 0;
}

VideoCaptureOhos::VideoCaptureOhos(CaptureType type,  bool enable_auto_rotation)
  : captureType_(type) 
  , enable_auto_rotation_(enable_auto_rotation){
  
  if (enable_auto_rotation_) {
    webrtc::DisplayManagerOhos::GetInstance()->Init();
  }
  RTC_LOG(LS_INFO) << "Ctor enable_auto_rotation:" << enable_auto_rotation_;
  ImageReceiverOnManager::GetInstance().cameraCapturer_ = this;
}

VideoCaptureOhos::~VideoCaptureOhos() { 
  RTC_LOG(LS_INFO) << "~VideoCaptureOhos ";
  ImageReceiverOnManager::GetInstance().cameraCapturer_ = nullptr;
  StopCapture();
  DeleteCameraOutputCapability();
  DeleteCameras();
  DeleteCameraManage();

  camera_dev_index_ = 0;
  is_init_ = false;
}

void CameraManagerStatusCallback(Camera_Manager* cameraManager, Camera_StatusInfo* status)
{

  RTC_LOG(LS_INFO) << "CameraManagerStatusCallback is called status: " << status->status << " ,cameraManager: " << cameraManager;
  if(ImageReceiverOnManager::GetInstance().cameraCapturer_ != nullptr && 
    ImageReceiverOnManager::GetInstance().cameraCapturer_->camera_manager_ == cameraManager) {
      ImageReceiverOnManager::GetInstance().cameraCapturer_->OnCameraStatusChange(status->status);
    }
  
}
CameraManager_Callbacks* GetCameraManagerListener()
{
   static CameraManager_Callbacks cameraManagerListener = {
      .onCameraStatus = CameraManagerStatusCallback
   };
   return &cameraManagerListener;
}

int32_t VideoCaptureOhos::Init(const char* device_id) {
  RTC_DCHECK_RUN_ON(&api_checker_);
  RTC_LOG(LS_INFO) << "Open device: " << device_id;
  int32_t return_code = -1;

  const int32_t nameLength = (int32_t)strlen((char*)device_id);
  _deviceUniqueId = new (std::nothrow) char[nameLength + 1];
  memcpy(_deviceUniqueId, device_id, nameLength + 1);

  Camera_ErrorCode ret = OH_Camera_GetCameraManager(&camera_manager_);
  if (camera_manager_ == nullptr || ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "Open OH_Camera_GetCameraManager failed! ret: " << ret;
    return return_code;
  }

  OH_CameraManager_RegisterCallback(camera_manager_, GetCameraManagerListener());

  ret = OH_CameraManager_GetSupportedCameras(camera_manager_, &cameras_, &cameras_size_);
  if(cameras_ == nullptr || ret != CAMERA_OK || cameras_size_ <= 0) {
    RTC_LOG(LS_ERROR) << "Open OH_CameraManager_GetSupportedCameras failed! ret: " << ret;
    return return_code;
  }

  for (uint32_t index = 0; index < cameras_size_; index++) {
    if(strcmp(cameras_[index].cameraId, device_id) == 0) {
      camera_dev_index_ = index;
      RTC_LOG(LS_INFO) << "cameraId: " << cameras_[index].cameraId << " ,cameraPosition: " << cameras_[index].cameraPosition
                        << " ,cameraType: " << cameras_[index].cameraType << " ,connectionType: " << cameras_[index].connectionType;
      break;
    }
  }

  ret = OH_CameraManager_GetSupportedCameraOutputCapability(camera_manager_, &cameras_[camera_dev_index_], &camera_output_capability_);
  if(camera_output_capability_ == nullptr || ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "Open OH_CameraManager_GetSupportedCameraOutputCapability failed! ret: " << ret;
    return return_code;
  }

  if(is_video_output_){
    if(camera_output_capability_->videoProfilesSize <= 0) {
      RTC_LOG(LS_ERROR) << "CameraVideoCapturer::Open videoProfilesSize <= 0";
      return return_code;
    }
  }
  else{
    if(camera_output_capability_->previewProfilesSize <= 0) {
      RTC_LOG(LS_ERROR) << "Open previewProfilesSize <= 0";    
      return return_code;
    }
  }


  UpdateSensorOrientation();
 
  return_code = CameraInputCreateAndOpen();
  return return_code;
}

int32_t VideoCaptureOhos::StartCapture(const VideoCaptureCapability& capability) {
  RTC_DCHECK_RUN_ON(&api_checker_);

  _requestedCapability = capability;
  RTC_LOG(LS_INFO) << "StartCapture width: " << _requestedCapability.width << " , height: " << _requestedCapability.height
                   << " , fps: " << _requestedCapability.maxFPS;
  int32_t ret = CAMERA_OK;
  if(is_camera_started_) {
    RTC_LOG(LS_WARNING) << "StartCapture camera is already running ";
    return ret;
  }

  if (captureType_ == CaptureType::BUFFER) {
    ret = InitImageReceiver(_requestedCapability.width, _requestedCapability.height);
    if(ret != IMAGE_SUCCESS) {
      return ret;
    }
  }
  
  if (camera_input_ == nullptr) {
    ret = CameraInputCreateAndOpen();
    if(ret!= CAMERA_OK) {
      return ret;
    }
  }
  if(is_video_output_){
    if (video_output_ == nullptr) {
      ret = VideoOutputCreate(_requestedCapability.width,_requestedCapability.height,_requestedCapability.maxFPS);
      if(ret != CAMERA_OK) {
        return ret;
      }
    }
  }
  else{
    if (preview_output_ == nullptr) {
      ret = PreviewOutputCreate(_requestedCapability.width, _requestedCapability.height,_requestedCapability.maxFPS);
      if(ret != CAMERA_OK) {
        return ret;
      }
    }
  }

  if (capture_session_ == nullptr) {
    ret = CaptureSessionCreate();
    if(ret != CAMERA_OK) {
      return ret;
    }
  }

  // SetCaptureFps(20);//capability.maxFPS
  // fps_ = 20;
  

  RTC_LOG(LS_INFO) << "StartCapture OH_CaptureSession_Start";
  ret = OH_CaptureSession_Start(capture_session_);
  if(ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "StartCapture OH_CaptureSession_Start failed! ret: " << ret;
    return ret;
  }
  if(is_video_output_){
    ret = OH_VideoOutput_Start(video_output_);
    if(ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "Start OH_VideoOutput_Start failed! ret: " << ret;
      return ret;
    }
  }

  RTC_LOG(LS_INFO) << "StartCapture success";
  is_camera_started_ = true;
  return ret;
}

int32_t VideoCaptureOhos::StopCapture() {
  RTC_LOG(LS_INFO) << "StopCapture";
  Camera_ErrorCode ret = CAMERA_OK;

  if(is_video_output_){
    if (video_output_ != nullptr) {
      ret = OH_VideoOutput_Stop(video_output_);
      if(ret != CAMERA_OK) {
        RTC_LOG(LS_ERROR) << "Stop OH_VideoOutput_Stop failed! ret: " << ret;
        return ret;
      }
    }
  }


  if(capture_session_ != nullptr) {
    ret = OH_CaptureSession_Stop(capture_session_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "StopCapture failed! ret: " << ret;
      return ret;
    }
  }

  is_camera_started_ = false;
  if(is_video_output_){
    VideoOutputRelease();
  }
  else{
    PreviewOutputRelease();
  }
  CameraInputRelease();
  CaptureSessionRelease();

  return ret;
}

bool VideoCaptureOhos::CaptureStarted() {
  return is_camera_started_;
}

int32_t VideoCaptureOhos::CaptureSettings(VideoCaptureCapability& settings) {
  RTC_DCHECK_RUN_ON(&api_checker_);
  settings = _requestedCapability;
  return 0;
}

void VideoOutputOnFrameStart(Camera_VideoOutput* videoOutput)
{
  RTC_LOG(LS_INFO) << "VideoOutputOnFrameStart videoOutput: " << videoOutput;
}

void VideoOutputOnFrameEnd(Camera_VideoOutput* videoOutput, int32_t frameCount)
{
  RTC_LOG(LS_INFO) << "VideoOutputOnFrameEnd videoOutput: " << videoOutput << " ,frameCount: " << frameCount;
}

void VideoOutputOnError(Camera_VideoOutput* videoOutput, Camera_ErrorCode errorCode)
{
  RTC_LOG(LS_ERROR) << "VideoOutputOnError videoOutput: " << videoOutput << " ,errorCode: " << errorCode;
}

VideoOutput_Callbacks* GetVideoOutputListener(void)
{
    static VideoOutput_Callbacks videoOutputListener = {
        .onFrameStart = VideoOutputOnFrameStart,
        .onFrameEnd = VideoOutputOnFrameEnd,
        .onError = VideoOutputOnError
    };
    return &videoOutputListener;
}

int32_t VideoCaptureOhos::SetCaptureFps(int fps){
  if(is_video_output_){
    if (video_output_ == nullptr) {
      return -1;
    }
  }
  else{
    if (preview_output_ == nullptr) {
      return -1;
    }
  }

  Camera_FrameRateRange* frameRateRanges;
  uint32_t size = 0;
  Camera_ErrorCode ret;
  if(is_video_output_){
    ret = OH_VideoOutput_GetSupportedFrameRates(video_output_, &frameRateRanges, &size);
  }
  else{
    ret = OH_PreviewOutput_GetSupportedFrameRates(preview_output_, &frameRateRanges, &size);
  }
  if (ret == CAMERA_OK && size > 0 && frameRateRanges != nullptr) {
    uint32_t minDiff = 0xFFFFFFFF;
    bool hasHigherFps = false;
    Camera_FrameRateRange targetFpsRange;
    for (uint32_t index = 0; index < size; index++) {
      Camera_FrameRateRange range = frameRateRanges[index];
      if(hasHigherFps && range.max < (uint32_t)fps) {
        continue;
      }
      uint32_t diff = (std::abs((int)range.min - fps) + std::abs((int)range.max - fps));
      if(!hasHigherFps && range.max >= (uint32_t)fps) {
        targetFpsRange = range;
        minDiff = diff;
        hasHigherFps = true;
      } else if(diff < minDiff){
        targetFpsRange = range;
        minDiff = diff;
      }
    }
    if (minDiff != 0xFFFFFFFF) {
      RTC_LOG(LS_INFO) << "VideoCaptureOhos::SetCaptureFps get best fps range [" << targetFpsRange.min << ", " << targetFpsRange.max << "]";
      if (fps >= targetFpsRange.min && fps <= targetFpsRange.max) {
        targetFpsRange.min = fps;
        targetFpsRange.max = fps;
      }
      if(is_video_output_){
        ret = OH_VideoOutput_SetFrameRate(video_output_, targetFpsRange.min, targetFpsRange.max);
        if (ret != CAMERA_OK) {
          RTC_LOG(LS_ERROR) << "VideoCaptureOhos::SetCaptureFps OH_VideoOutput_SetFrameRate failed! ret: " << ret;
          return -1;
        }
      }
      else{
        ret = OH_PreviewOutput_SetFrameRate(preview_output_, targetFpsRange.min, targetFpsRange.max);
        if (ret != CAMERA_OK) {
          RTC_LOG(LS_ERROR) << "VideoCaptureOhos::SetCaptureFps OH_PreviewOutput_SetFrameRate failed! ret: " << ret;
          return -1;
        }
      }
      

      return 0;
    }
  } else {
    RTC_LOG(LS_ERROR) << "VideoCaptureOhos::SetCaptureFps OH_VideoOutput_GetSupportedFrameRates failed! ret: " << ret << ", size: " << size << ", frameRateRanges: " << frameRateRanges;
  }
  return -1;
}

int32_t  VideoCaptureOhos::SetCaptureRotation(VideoRotation rotation) {
  RTC_LOG(LS_INFO) << "SetCaptureRotation " << (int)rotation;
  if(rotation == kVideoRotation_0 ||
     rotation == kVideoRotation_90 ||
     rotation == kVideoRotation_180 ||
     rotation == kVideoRotation_270) {
    rotation_ = rotation;
    return 0;
  }
  return -1;
}

void VideoCaptureOhos::UpdateSensorOrientation() {
  uint32_t orientation = 0;
  if (cameras_ != nullptr && cameras_size_ > 0) {
    Camera_ErrorCode ret = OH_CameraDevice_GetCameraOrientation(&cameras_[camera_dev_index_], &orientation);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "UpdateSensorOrientation OH_CameraDevice_GetCameraOrientation failed! ret: " << ret;
      return;
    }
  } else {
    RTC_LOG(LS_ERROR) << "UpdateSensorOrientation cameras_ null";
    return;
  }
  RTC_LOG(LS_INFO) << "UpdateSensorOrientation " << orientation;
  sensor_orientation_ = orientation;
}

int VideoCaptureOhos::GetFrameRotation() {

  if (cameras_ == nullptr || cameras_size_ <= 0) {
    RTC_LOG(LS_ERROR) << "GetFrameRotation cameras_ null";
    return 0;
  }
  int rotation = rotation_;
  if (enable_auto_rotation_) {
    NativeDisplayManager_Rotation displayRotation = webrtc::DisplayManagerOhos::GetInstance()->GetDefaultDisplayRotation();
    switch(displayRotation) {
      case DISPLAY_MANAGER_ROTATION_0:
        rotation = 0;
        break;
      case DISPLAY_MANAGER_ROTATION_90:
        rotation = 270;
        break;
      case DISPLAY_MANAGER_ROTATION_180:
        rotation = 180;
        break;
      case DISPLAY_MANAGER_ROTATION_270:
        rotation = 90;
        break;
    }
  }
  if (cameras_[camera_dev_index_].cameraPosition == CAMERA_POSITION_BACK) {
    rotation = 360 - rotation;
  }
  return (rotation + sensor_orientation_) % 360;
}

void OnCameraInputError(const Camera_Input *cameraInput, Camera_ErrorCode errorCode) {
  RTC_LOG(LS_ERROR) << "OnCameraInputError errorCode: " << errorCode << " ,cameraInput: " << cameraInput;
}

CameraInput_Callbacks *GetCameraInputListener(void) {
  static CameraInput_Callbacks cameraInputCallbacks = {.onError = OnCameraInputError};
  return &cameraInputCallbacks;
}

int32_t VideoCaptureOhos::CameraInputCreateAndOpen() {
  RTC_LOG(LS_INFO) << "CameraInputCreateAndOpen";

  Camera_ErrorCode ret;
  ret = OH_CameraManager_CreateCameraInput(camera_manager_, &cameras_[camera_dev_index_], &camera_input_);
  if (camera_input_ == nullptr || ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CameraInputCreateAndOpen OH_CameraManager_CreateCameraInput failed! ret: " << ret;
      return ret;
  }

  OH_CameraInput_RegisterCallback(camera_input_, GetCameraInputListener());
  ret = OH_CameraInput_Open(camera_input_);
  if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CameraInputCreateAndOpen OH_CameraInput_Open failed! ret: " << ret;
      return ret;
  }
  
  return ret;
}

void PreviewOutputOnFrameStart(Camera_PreviewOutput* previewOutput)
{
  RTC_LOG(LS_INFO) << "PreviewOutputOnFrameStart previewOutput: " << previewOutput;
}

void PreviewOutputOnFrameEnd(Camera_PreviewOutput* previewOutput, int32_t frameCount)
{
  RTC_LOG(LS_INFO) << "PreviewOutputOnFrameEnd previewOutput: " << previewOutput << " ,frameCount: " << frameCount;
}

void PreviewOutputOnError(Camera_PreviewOutput* previewOutput, Camera_ErrorCode errorCode)
{
  RTC_LOG(LS_ERROR) << "PreviewOutputOnError previewOutput: " << previewOutput << " ,errorCode: " << errorCode;
}

PreviewOutput_Callbacks* GetPreviewOutputListener(void)
{
    static PreviewOutput_Callbacks previewOutputListener = {
        .onFrameStart = PreviewOutputOnFrameStart,
        .onFrameEnd = PreviewOutputOnFrameEnd,
        .onError = PreviewOutputOnError
    };
    return &previewOutputListener;
}

Camera_Profile* GetClosestSupportedPreviewProfile(Camera_OutputCapability* cameraOutputCapability, uint32_t requestedWidth, uint32_t requestedHeight) {
    Camera_Profile* closestProfile = nullptr;
    double closestRatioDiff = std::numeric_limits<double>::max();
    uint32_t closestAreaDiff = std::numeric_limits<uint32_t>::max();
    double requestedRatio = static_cast<double>(requestedWidth) / requestedHeight;

    for (uint32_t i = 0; i < cameraOutputCapability->previewProfilesSize; ++i) {
        Camera_Profile* profile = cameraOutputCapability->previewProfiles[i];
        
        // Skip profile if it does not match the required format
        if(profile->format != CAMERA_FORMAT_YUV_420_SP) {
            continue;
        }

        double profileRatio = static_cast<double>(profile->size.width) / profile->size.height;
        uint32_t profileArea = profile->size.width * profile->size.height;
        uint32_t requestedArea = requestedWidth * requestedHeight;
        
        double ratioDiff = std::abs(requestedRatio - profileRatio);
        uint32_t areaDiff = profileArea >= requestedArea ? profileArea - requestedArea : requestedArea - profileArea;
        
        // If profile matches the requested area and ratio exactly, use it and stop searching
        if (profile->size.width == requestedWidth && profile->size.height == requestedHeight) {
            closestProfile = profile;
            break;
        }

        // Otherwise, prefer profiles with bigger or equal area to requested and the least ratio difference
        if (profile->size.width >= requestedWidth && profile->size.height >= requestedHeight) {
            if (ratioDiff < closestRatioDiff || 
                (ratioDiff == closestRatioDiff && areaDiff < closestAreaDiff)) {
                closestRatioDiff = ratioDiff;
                closestAreaDiff = areaDiff;
                closestProfile = profile;
            }
        }
    }

    // Fallback to the first available profile if no better match found
    return closestProfile ? closestProfile : cameraOutputCapability->previewProfiles[0];
}

Camera_VideoProfile* GetClosestSupportedVideoProfile(Camera_OutputCapability* cameraOutputCapability, uint32_t requestedWidth, uint32_t requestedHeight) {
    Camera_VideoProfile* closestProfile = nullptr;
    double closestRatioDiff = std::numeric_limits<double>::max();
    uint32_t closestAreaDiff = std::numeric_limits<uint32_t>::max();
    double requestedRatio = static_cast<double>(requestedWidth) / requestedHeight;

    for (uint32_t i = 0; i < cameraOutputCapability->videoProfilesSize; ++i) {
        Camera_VideoProfile* profile = cameraOutputCapability->videoProfiles[i];
        
        // Skip profile if it does not match the required format
        if(profile->format != CAMERA_FORMAT_YUV_420_SP) {
            continue;
        }

        double profileRatio = static_cast<double>(profile->size.width) / profile->size.height;
        uint32_t profileArea = profile->size.width * profile->size.height;
        uint32_t requestedArea = requestedWidth * requestedHeight;
        
        double ratioDiff = std::abs(requestedRatio - profileRatio);
        uint32_t areaDiff = profileArea >= requestedArea ? profileArea - requestedArea : requestedArea - profileArea;
        
        // If profile matches the requested area and ratio exactly, use it and stop searching
        if (profile->size.width == requestedWidth && profile->size.height == requestedHeight) {
            closestProfile = profile;
            break;
        }

        // Otherwise, prefer profiles with bigger or equal area to requested and the least ratio difference
        if (profile->size.width >= requestedWidth && profile->size.height >= requestedHeight) {
            if (ratioDiff < closestRatioDiff || 
                (ratioDiff == closestRatioDiff && areaDiff < closestAreaDiff)) {
                closestRatioDiff = ratioDiff;
                closestAreaDiff = areaDiff;
                closestProfile = profile;
            }
        }
    }

    // Fallback to the first available profile if no better match found
    return closestProfile ? closestProfile : cameraOutputCapability->videoProfiles[0];
}


int32_t VideoCaptureOhos::VideoOutputCreate(int width, int height, int fps) {
  RTC_LOG(LS_INFO) << "VideoOutputCreate";

  video_profile_ = GetClosestSupportedVideoProfile(camera_output_capability_,width,height);
  RTC_LOG(LS_INFO) << "GetClosestSupportedVideoProfile size: " << video_profile_->size.width << "x" << video_profile_->size.height << " ,format: " << video_profile_->format;
  char surfaceID[24];
  uint64_t id;
  if (captureType_ == CaptureType::BUFFER) {
    id = GetImageReceiverID();
    RTC_LOG_T(LS_INFO) <<"VideoOutputCreate id for buffer capture: " << id;
  } else {
    if (InitRenderContext() == false) {
      RTC_LOG_T(LS_ERROR) <<"VideoOutputCreate InitRenderContext failed";
      return -1;
    }

    if (CreateNativeImage() == false) {
      RTC_LOG_T(LS_ERROR) <<"VideoOutputCreate CreateNativeImage failed";
      return -1;
    }

    if (CreateNativeWindow() == false) {
        RTC_LOG_T(LS_ERROR) <<"VideoOutputCreate CreateNativeWindow failed";
        return -1;
    }
    int32_t rr = OH_NativeImage_GetSurfaceId(nativeImage_, &id);
    if (rr != 0) {
        RTC_LOG_T(LS_ERROR) <<"VideoOutputCreate OH_NativeImage_GetSurfaceId failed " << rr;
        return -1;
    }
    RTC_LOG_T(LS_INFO) <<"VideoOutputCreate id for texture capture: " << id;
  }

  snprintf(surfaceID, 24, "%lu", id);

  Camera_ErrorCode ret = OH_CameraManager_CreateVideoOutput(camera_manager_, video_profile_, surfaceID, &video_output_);
  if (video_output_ == nullptr || ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "VideoOutputCreate OH_CameraManager_CreateVideoOutput failed! ret: " << ret;
    return ret;
  }

  OH_VideoOutput_RegisterCallback(video_output_, GetVideoOutputListener());
  return ret;
}


int32_t VideoCaptureOhos::PreviewOutputCreate(int width, int height, int fps) {
  RTC_LOG(LS_INFO) << "PreviewOutputCreate";

  preview_profile_ = GetClosestSupportedPreviewProfile(camera_output_capability_,width,height);
  RTC_LOG(LS_INFO) << "GetClosestSupportedPreviewProfile size: " << preview_profile_->size.width << "x" << preview_profile_->size.height << " ,format: " << preview_profile_->format;
  char surfaceID[24];
  uint64_t id;
  if (captureType_ == CaptureType::BUFFER) {
    id = GetImageReceiverID();
    RTC_LOG_T(LS_INFO) <<"PreviewOutputCreate id for buffer capture: " << id;
  } else {
    if (InitRenderContext() == false) {
      RTC_LOG_T(LS_ERROR) <<"PreviewOutputCreate InitRenderContext failed";
      return -1;
    }

    if (CreateNativeImage() == false) {
      RTC_LOG_T(LS_ERROR) <<"PreviewOutputCreate CreateNativeImage failed";
      return -1;
    }

    if (CreateNativeWindow() == false) {
        RTC_LOG_T(LS_ERROR) <<"PreviewOutputCreate CreateNativeWindow failed";
        return -1;
    }
    int32_t rr = OH_NativeImage_GetSurfaceId(nativeImage_, &id);
    if (rr != 0) {
        RTC_LOG_T(LS_ERROR) <<"PreviewOutputCreate OH_NativeImage_GetSurfaceId failed " << rr;
        return -1;
    }
    RTC_LOG_T(LS_INFO) <<"PreviewOutputCreate id for texture capture: " << id;
  }

  snprintf(surfaceID, 24, "%lu", id);

  Camera_ErrorCode ret = OH_CameraManager_CreatePreviewOutput(camera_manager_, preview_profile_, surfaceID, &preview_output_);
  if (preview_output_ == nullptr || ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "PreviewOutputCreate OH_CameraManager_CreatePreviewOutput failed! ret: " << ret;
    return ret;
  }
  OH_PreviewOutput_RegisterCallback(preview_output_, GetPreviewOutputListener());
  return ret;
}

int32_t VideoCaptureOhos::CaptureSessionCreate() {
  RTC_LOG(LS_INFO) << "CaptureSessionCreate";
  Camera_ErrorCode ret;
  ret = OH_CameraManager_CreateCaptureSession(camera_manager_, &capture_session_);
  if (capture_session_ == nullptr || ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "CaptureSessionCreate failed! ret: " << ret;
  }

  RTC_LOG(LS_INFO) << "CaptureSessionCreate OH_CaptureSession_BeginConfig";
  ret = OH_CaptureSession_BeginConfig(capture_session_);
  if (ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "CaptureSessionCreate OH_CaptureSession_BeginConfig failed! ret: " << ret;
    return ret;
  }

  ret = OH_CaptureSession_AddInput(capture_session_, camera_input_);
  if (ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "CaptureSessionCreate OH_CaptureSession_AddInput failed! ret: " << ret;
    return ret;
  }

  if(is_video_output_){
    ret = OH_CaptureSession_AddVideoOutput(capture_session_, video_output_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CaptureSessionCreate OH_CaptureSession_AddVideoOutput failed! ret: " << ret;
      return ret;
    }
  }
  else
  {
    ret = OH_CaptureSession_AddPreviewOutput(capture_session_, preview_output_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CaptureSessionCreate OH_CaptureSession_AddPreviewOutput failed! ret: " << ret;
      return ret;
    }
  }

  ret = OH_CaptureSession_CommitConfig(capture_session_);
  if (ret != CAMERA_OK) {
    RTC_LOG(LS_ERROR) << "CaptureSessionCreate OH_CaptureSession_CommitConfig failed! ret: " << ret;
    return ret;
  }
  
  return ret;
}

int32_t VideoCaptureOhos::VideoOutputRelease() {
  RTC_LOG(LS_INFO) << "VideoOutputRelease";
  Camera_ErrorCode ret = CAMERA_OK;
  if (video_output_ != nullptr) {
    ret = OH_VideoOutput_Release(video_output_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "VideoOutputRelease OH_VideoOutput_Release failed! ret: " << ret;
    }
  }

  if (captureType_ == CaptureType::SURFACE) {
    DestroyNativeWindow();
    DestroyNativeImage();
    DestroyRenderContext();
  }

  video_output_ = nullptr;
  return ret;
}

int32_t VideoCaptureOhos::CameraInputRelease() {
  RTC_LOG(LS_INFO) << "CameraInputRelease";
  Camera_ErrorCode ret = CAMERA_OK;
  if (camera_input_ != nullptr) {
    if(is_video_output_){
      OH_VideoOutput_UnregisterCallback(video_output_, GetVideoOutputListener());
    }
    else{
      OH_PreviewOutput_UnregisterCallback(preview_output_, GetPreviewOutputListener());
    }
    
    OH_CameraInput_UnregisterCallback(camera_input_, GetCameraInputListener());
    ret = OH_CameraInput_Close(camera_input_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CameraInputRelease OH_CameraInput_Close failed! ret: " << ret;
    }
    ret = OH_CameraInput_Release(camera_input_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CameraInputRelease OH_CameraInput_Release failed! ret: " << ret;
    }
  }
  
  camera_input_ = nullptr;
  return ret;
}

int32_t VideoCaptureOhos::PreviewOutputRelease() {
  RTC_LOG(LS_INFO) << "PreviewOutputRelease";
  Camera_ErrorCode ret = CAMERA_OK;
  if (preview_output_ != nullptr) {
    ret = OH_PreviewOutput_Release(preview_output_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "PreviewOutputRelease OH_PreviewOutput_Release failed! ret: " << ret;
    }
  }

  if (captureType_ == CaptureType::SURFACE) {
    DestroyNativeWindow();
    DestroyNativeImage();
    DestroyRenderContext();
  }

  preview_output_ = nullptr;
  return ret;
}

int32_t VideoCaptureOhos::CaptureSessionRelease() {
  RTC_LOG(LS_INFO) << "CaptureSessionRelease";

  Camera_ErrorCode ret = CAMERA_OK;
  if (capture_session_ != nullptr) {
    ret = OH_CaptureSession_Release(capture_session_);
    if (ret != CAMERA_OK) {
      RTC_LOG(LS_ERROR) << "CaptureSessionRelease OH_CaptureSession_Release failed! ret: " << ret;
    }
    capture_session_ = nullptr;
  }
  return ret;
}

int32_t VideoCaptureOhos::DeleteCameraOutputCapability() {
  if (camera_output_capability_ != nullptr) {
    if(is_video_output_){
      video_profile_ = nullptr;
    }
    else{
      preview_profile_ = nullptr;
    }
    camera_output_capability_ = nullptr;
  }
  
  return 0;
}

int32_t VideoCaptureOhos::DeleteCameras() {
  if (cameras_ != nullptr) {
    cameras_ = nullptr;
    cameras_size_ = 0;
  }
  
  return 0;
}

int32_t VideoCaptureOhos::DeleteCameraManage() {
  if (camera_manager_ != nullptr) {
    OH_CameraManager_UnregisterCallback(camera_manager_, GetCameraManagerListener());
    camera_manager_ = nullptr;
  }
  
  return 0;
}

void VideoCaptureOhos::OnCameraStatusChange(Camera_Status status) {
  if(status == CAMERA_STATUS_AVAILABLE) {
    if(is_camera_started_) {
      RTC_LOG(LS_ERROR) << "OnCameraStatusChange The camera has been activated, and now the camera status has changed to AVAILABLE, which must have been stopped by the system";
    }
  } 
}

void VideoCaptureOhos::OnNativeImageFrameAvailable(void *data)
{
    VideoCaptureOhos* desktop = static_cast<VideoCaptureOhos*>(data);
    desktop->OnNativeFrameAvailable();
    RTC_LOG(LS_VERBOSE) << "OnNativeImageFrameAvailable called";
}

int32_t VideoCaptureOhos::OnNativeFrameAvailable()
{
    int32_t ret;
    gl_context_->MakeCurrent(eglSurface_);
    RTC_LOG(LS_VERBOSE) << "OnNativeFrameAvailable MakeCurrent " << GetEglErrorString();
    {
        std::lock_guard<std::mutex> lock(*(OhosEGLContextManager::GetInstance().GetTextureMutex(nativeImageTextureID_)));
        ret = OH_NativeImage_UpdateSurfaceImage(nativeImage_);
        RTC_LOG(LS_VERBOSE) << "OnNativeFrameAvailable OH_NativeImage_UpdateSurfaceImage "
                         << GetGLErrorString();
        RTC_LOG(LS_VERBOSE) << "OnNativeFrameAvailable ImageTextureID = " << nativeImageTextureID_;
    }

    gl_context_->MakeCurrent();
    RTC_LOG(LS_VERBOSE) << "OnNativeFrameAvailable MakeEmptyCurrent " << GetEglErrorString();

    if (ret != 0) {
        RTC_LOG(LS_ERROR) << "OnNativeFrameAvailable OH_NativeImage_UpdateSurfaceImage failed, ret: " << ret;
        return -1;
    }

    // create video frame
    int width = 0, height = 0;
    if(is_video_output_){
      width = video_profile_->size.width;
      height = video_profile_->size.height;
    }
    else{
      width = preview_profile_->size.width;
      height = preview_profile_->size.height;
    }

    rtc::scoped_refptr<OhosVideoBuffer> textureBuffer = OhosVideoBuffer::Create(width, height, textureBuffer_);
    webrtc::VideoFrame video_frame = webrtc::VideoFrame::Builder()
                                       .set_video_frame_buffer(textureBuffer)
                                       .set_timestamp_rtp(0)
                                       .set_timestamp_ms(rtc::TimeMillis())
                                       .set_rotation(webrtc::VideoRotation(GetFrameRotation()))
                                       .build();

    debug_log_counter_ ++;
    if (debug_log_counter_%_requestedCapability.maxFPS == 1 || _requestedCapability.maxFPS<= 1) {
        RTC_LOG_T(LS_INFO) << "OnNativeFrameAvailable, videoframe, w:" << width << ", h:" << height
                          << ", rotation:" << GetFrameRotation()
                         << ", cnt:" << debug_log_counter_;
    }

    IncomingFrame(video_frame);
    return 0;
}

bool VideoCaptureOhos::InitRenderContext()
{

    gl_context_ = std::make_unique<EGLRenderContext>();
    auto contextResource = OhosEGLContextManager::GetInstance().GetEGLContextResource();
    if (contextResource == nullptr) {
        RTC_LOG(LS_INFO) << "InitRenderContext GetEGLContextResource failed";
        return false;
    }
    if (!gl_context_->Init(contextResource->eglContext_->GetEGLContext())) {
      RTC_LOG_T(LS_ERROR) << "InitRenderContext init EGL failed!";
      return false;
    }

    // 创建eglSurface_
    int width = 0, height = 0;
    if(is_video_output_){
      width = video_profile_->size.width;
      height = video_profile_->size.height; 
    }
    else{
      width = preview_profile_->size.width;
      height = preview_profile_->size.height;
    }

    EGLint pBufferAttributes[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
    eglSurface_ = gl_context_->CreatePBufferEglSurface(pBufferAttributes);
    if (eglSurface_ == EGL_NO_SURFACE) {
        RTC_LOG(LS_INFO) << "InitRenderContext CreateEglSurface " << GetEglErrorString();
        return false;
    }

    return true;
}

int32_t VideoCaptureOhos::DestroyRenderContext()
{
    if (eglSurface_ != EGL_NO_SURFACE && gl_context_ != nullptr) {
        gl_context_->DestroyEglSurface(eglSurface_);
        eglSurface_ = EGL_NO_SURFACE;
    }
    gl_context_.reset();

    return 0;
}

bool VideoCaptureOhos::CreateNativeWindow()
{
    nativeWindow_ = OH_NativeImage_AcquireNativeWindow(nativeImage_);
    if (nativeWindow_ == nullptr) {
        RTC_LOG(LS_ERROR) << "CreateNativeWindow OH_NativeImage_AcquireNativeWindow failed";
        return false;
    }

    int width = 0, height = 0;
    if(is_video_output_){
      width = video_profile_->size.width;
      height = video_profile_->size.height; 
    }
    else{
      width = preview_profile_->size.width;
      height = preview_profile_->size.height;
    }
    int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_BUFFER_GEOMETRY, width, height);
    if (ret != 0) {
        RTC_LOG(LS_ERROR) << "CreateNativeWindow OH_NativeWindow_NativeWindowHandleOpt failed";
        return false;
    }

    return true;
}

int32_t VideoCaptureOhos::DestroyNativeWindow()
{
    RTC_LOG(LS_INFO) << "DestroyNativeWindow started";
    if (nativeWindow_ != nullptr) {
        OH_NativeWindow_DestroyNativeWindow(nativeWindow_);
        nativeWindow_ = nullptr;
    }
    RTC_LOG(LS_INFO) << "DestroyNativeWindow end";
    return 0;
}

bool VideoCaptureOhos::CreateNativeImage()
{
    RTC_LOG(LS_INFO) << "CreateNativeImage started";
    gl_context_->MakeCurrent(eglSurface_);
    RTC_LOG(LS_INFO) << "CreateNativeImage MakeCurrent "
                     << GetEglErrorString();

    glGenTextures(1, &nativeImageTextureID_);
    RTC_LOG(LS_INFO) << "CreateNativeImage textureID = " << nativeImageTextureID_;
    RTC_LOG(LS_INFO) << "CreateNativeImage glGenTextures " << GetGLErrorString();

    glBindTexture(GL_TEXTURE_EXTERNAL_OES, nativeImageTextureID_);
    RTC_LOG(LS_INFO) << "CreateNativeImage glBindTexture " << GetGLErrorString();

    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_REPEAT);
    RTC_LOG(LS_INFO) << "CreateNativeImage glTexParameteri  " << GetGLErrorString();
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_REPEAT);
    RTC_LOG(LS_INFO) << "CreateNativeImage glTexParameteri " << GetGLErrorString();

    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    RTC_LOG(LS_INFO) << "CreateNativeImage glTexParameteri " << GetGLErrorString();
    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    RTC_LOG(LS_INFO) << "CreateNativeImage glTexParameteri " << GetGLErrorString();

    nativeImage_ = OH_NativeImage_Create(nativeImageTextureID_, GL_TEXTURE_EXTERNAL_OES);
    RTC_LOG(LS_INFO) << "CreateNativeImage OH_NativeImage_Create " << GetGLErrorString();

    glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
    RTC_LOG(LS_INFO) << "CreateNativeImage glBindTexture " << GetGLErrorString();

    gl_context_->MakeCurrent();
    RTC_LOG(LS_INFO) << "CreateNativeImage MakeEmptyCurrent " << GetEglErrorString();

    if (nativeImage_ == nullptr) {
        RTC_LOG(LS_ERROR) << "CreateNativeImage OH_NativeImage_Create failed.";
        return false;
    }

    nativeImageFrameAvailableListener_.context = this;
    nativeImageFrameAvailableListener_.onFrameAvailable = &VideoCaptureOhos::OnNativeImageFrameAvailable;
    int32_t ret = OH_NativeImage_SetOnFrameAvailableListener(nativeImage_, nativeImageFrameAvailableListener_);
    if (ret != 0) {
        RTC_LOG(LS_ERROR) << "CreateNativeImage OH_NativeImage_SetOnFrameAvailableListener failed, ret is " << ret;
        return false;
    }

    // set textrueBuffer_
    textureBuffer_.textureID = nativeImageTextureID_;
    textureBuffer_.type = OhosBufferType::OES;
    ret = OH_NativeImage_GetTransformMatrix(nativeImage_, textureBuffer_.matrix);
    if (ret != 0) {
        RTC_LOG(LS_ERROR) << "CreateNativeImage OH_NativeImage_GetTransformMatrix failed, ret is " << ret;
        return false;
    }

    RTC_LOG(LS_INFO) << "CreateNativeImage end";
    return true;
}

int32_t VideoCaptureOhos::DestroyNativeImage()
{
    RTC_LOG(LS_INFO) << "DestroyNativeImage start";

    if (nativeImage_ != nullptr) {
        (void)OH_NativeImage_UnsetOnFrameAvailableListener(nativeImage_);
        OH_NativeImage_Destroy(&nativeImage_);
        nativeImage_ = nullptr;
    }

    if (nativeImageTextureID_ != 0U && gl_context_ != nullptr) {
        gl_context_->MakeCurrent(eglSurface_);
        RTC_LOG(LS_INFO) << "DestroyNativeImage MakeCurrent " << GetEglErrorString();

        {
            std::lock_guard<std::mutex> lock(*(OhosEGLContextManager::GetInstance().GetTextureMutex(nativeImageTextureID_)));
            glDeleteTextures(1, &nativeImageTextureID_);
            nativeImageTextureID_ = 0U;
        }

        gl_context_->MakeCurrent();
        RTC_LOG(LS_INFO) << "DestroyNativeImage MakeEmptyCurrent " << GetEglErrorString();
    }

    RTC_LOG(LS_INFO) << "DestroyNativeImage end";
    return 0;
}

}  // namespace videocapturemodule
}  // namespace webrtc
